﻿using System;
using System.Collections.Generic;
using ObjectSync.Interfaces;
using Castle.DynamicProxy;
using ObjectSync.Util;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Text;
using Lidgren.Network;

namespace ObjectSync
{
    public struct MessageAnswer
    {
        public object answer;
        public long ID;
    }

    public class NoAnswer
    {

    }

    public static class RemoteManager
    {
        //#region SINGLETON
        //static readonly RemoteManager instance = new RemoteManager();

        //// Explicit static constructor to tell C# compiler
        //// not to mark type as beforefieldinit
        //static RemoteManager()
        //{
        //}

        //RemoteManager()
        //{
        //}

        //public static RemoteManager Instance
        //{
        //    get
        //    {
        //        return instance;
        //    }
        //}
        //#endregion

        /// <summary>
        /// Get the next unused netId number
        /// </summary>
        public static long NextRemoteID
        {
            get
            {
                return IDManager.Instance.NextRemoteID;
            }
        }

        private static List<MessageAnswer> Answers = new List<MessageAnswer>();

        private static readonly ProxyGenerator generator = new ProxyGenerator();

        public static TRemote createRemoteObject<TRemote>() where TRemote :  class,  IRemote, new()
        {
            return RemoteManager.createRemoteObject<TRemote>("", "");
        }

        public static TRemote createRemoteObject<TRemote>(String ExecutionAssembly) where TRemote : class, IRemote, new()
        {
            return RemoteManager.createRemoteObject<TRemote>(ExecutionAssembly, "");
        }

        public static TRemote createRemoteObject<TRemote>(String ExecutionAssembly, String ExecutionNameSpace) where TRemote : class, IRemote, new()
        {
            var interceptor = new RemoteObjectInterceptor();
            var proxy = generator.CreateClassProxy<TRemote>(interceptor);
            proxy.ExecutionAssembly = ExecutionAssembly;
            proxy.ExecutionNameSpace = ExecutionNameSpace;
            return proxy;
        }

        public static void RegisterAnswer(NetIncomingMessage message)
        {
            int msize = message.ReadInt32();
            MessageAnswer resp;
            if (msize >= 0)
            {
                byte[] serialized = message.ReadBytes(msize);


                var formatter = new BinaryFormatter();
                MemoryStream ms = new MemoryStream(serialized);

                resp = new MessageAnswer()
                {
                    answer = formatter.Deserialize(ms),
                    ID = message.ReadInt32()
                };
            }
            else
            {
                resp = new MessageAnswer()
                {
                    answer = null,
                    ID = message.ReadInt32()
                };
            }
            Answers.Add(resp);
            //return;
        }

		public static NetOutgoingMessage ExecuteAndEncode(NetIncomingMessage ExecMessage, NetOutgoingMessage respMessage)
        {

            // Fist let´s get the basic Data 

            //int asmSize = ExecMessage.ReadInt32();
            string asmName = ExecMessage.ReadString();

            //int tpSize = ExecMessage.ReadInt32();
            string tpName = ExecMessage.ReadString();

            //int mSize = ExecMessage.ReadInt32();
            string mName = ExecMessage.ReadString();

            int nParam = ExecMessage.ReadInt32();

            BinaryFormatter formatter;
            List<Object> parameters = new List<object>();
            for (int i = 0; i < nParam; i++)
            {
                int pSize = ExecMessage.ReadInt32();
                byte[] pSerialized = ExecMessage.ReadBytes(pSize);
                formatter = new BinaryFormatter();
                MemoryStream ms = new MemoryStream(pSerialized);
                parameters.Add(formatter.Deserialize(ms));
            }

            int ID = ExecMessage.ReadInt32();

            var tp = Assembly.Load(asmName).GetType(asmName.Split(',')[0] + "." + tpName);

            var mi = tp.GetMethod(mName);
            var ci = tp.GetConstructor(Type.EmptyTypes);
            var responder = ci.Invoke(null);

            object obj = Activator.CreateInstance(asmName, asmName.Split(',')[0] + "." + tpName);

            object result;
            // Getting the result 
            if(parameters.Count > 0 )
                result = mi.Invoke(responder, parameters.ToArray());
            else
                result = mi.Invoke(responder, null);
            
            Stream str = new MemoryStream();

            //NetBuffer respMessage = new NetBuffer();
			//NetOutgoingMessage respMessage = .;

            respMessage.Write((UInt16)MessageType.ExecResp);

            if (result != null)
            {
                formatter = new BinaryFormatter();
                formatter.Serialize(str, result);
                str.Position = 0;

                Byte[] outBuff = new Byte[str.Length];

                str.Read(outBuff, 0, (int)str.Length);

                respMessage.Write(outBuff.Length);
                respMessage.Write(outBuff);
            }
            else
            {
                respMessage.Write(-1);
                //respMessage.Write(outBuff);
            }

            respMessage.Write(ID);

            //string execResp = String.Format("EXECRESP${0}${1}", StringEncode.EconcodeToTransmit(enc.GetString(outBuff)), execOrder[i]); 


            return respMessage;

        }

        public static object getAnswer(long id)
        { 
            foreach(MessageAnswer ma in Answers)
            {
                if (ma.ID == id) return ma.answer;
            }

            return new NoAnswer();
        }


    }
}
